package com.sherllinhe.business.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
//import com.sherllinhe.common.context.LoginMemberContext;
import com.sherllinhe.business.enums.RedisKeyPreEnum;
import com.sherllinhe.business.mapper.cust.SkTokenMapperCust;
import com.sherllinhe.common.exception.BusinessException;
import com.sherllinhe.common.exception.BusinessExceptionEnum;
import com.sherllinhe.common.resp.PageResp;
import com.sherllinhe.common.util.SnowUtil;
import com.sherllinhe.business.domain.SkToken;
import com.sherllinhe.business.domain.SkTokenExample;
import com.sherllinhe.business.mapper.SkTokenMapper;
import com.sherllinhe.business.req.SkTokenQueryReq;
import com.sherllinhe.business.req.SkTokenSaveReq;
import com.sherllinhe.business.resp.SkTokenQueryResp;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 功能：
 * 作者：Sherllin He
 * 日期：
 */
@Slf4j
@Service
public class SkTokenService {
    @Autowired
    private SkTokenMapper skTokenMapper;
    @Autowired
    private DailyTrainSeatService dailyTrainSeatService;
    @Autowired
    private DailyTrainStationService dailyTrainStationService;
    @Autowired
    private SkTokenMapperCust skTokenMapperCust;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Value("${spring.profiles.active}")
    private String env;
    // 新增和编辑秒杀令牌
    public void save(SkTokenSaveReq req){
        DateTime now = DateTime.now();
        SkToken skToken = BeanUtil.copyProperties(req, SkToken.class);

        //判断是新增还是更新
        //skToken.setMemberId(LoginMemberContext.getId());
        if(ObjUtil.isNull(skToken.getId())) {
            //新增
            skToken.setId(SnowUtil.getSnowflakeNextId());
            skToken.setCreateTime(now);
            skToken.setUpdateTime(now);
            skTokenMapper.insert(skToken);
        }else{
            //更新
            skToken.setUpdateTime(now);
            skTokenMapper.updateByPrimaryKey(skToken);
        }


    }
    // 分页查询秒杀令牌列表
    public PageResp<SkTokenQueryResp> queryList(SkTokenQueryReq req){
        SkTokenExample example = new SkTokenExample();
        example.setOrderByClause("update_time desc");
        SkTokenExample.Criteria criteria = example.createCriteria();
        //if(ObjUtil.isNotNull(req.getMemberId())){
            //criteria.andMemberIdEqualTo(req.getMemberId());
        //}

        log.info("查询页码:{}",req.getPage());
        log.info("每页条数:{}",req.getSize());
        PageHelper.startPage(req.getPage(),req.getSize());
        List<SkToken> skTokenList = skTokenMapper.selectByExample(example);

        PageInfo<SkToken> pageInfo = new PageInfo<>(skTokenList);
        log.info("总条数:{}", pageInfo.getTotal());
        log.info("总页数:{}", pageInfo.getPages());

        List<SkTokenQueryResp> list = BeanUtil.copyToList(skTokenList, SkTokenQueryResp.class);

        PageResp<SkTokenQueryResp> pageResp = new PageResp<>();
        pageResp.setList(list);
        pageResp.setTotal(pageInfo.getTotal());

        return pageResp;
    }
    //删除秒杀令牌
    public void delete(Long id){
        skTokenMapper.deleteByPrimaryKey(id);
    }

    public void genDaily(Date date, String trainCode){
        log.info("删除日期【{}】车次【{}】的秒杀令牌开始", DateUtil.formatDate(date), trainCode);
        SkTokenExample example = new SkTokenExample();
        example.createCriteria()
                .andDateEqualTo(date)
                .andTrainCodeEqualTo(trainCode);
        skTokenMapper.deleteByExample(example);

        DateTime now = DateTime.now();
        SkToken skToken = new SkToken();
        skToken.setDate(date);
        skToken.setTrainCode(trainCode);
        skToken.setId(SnowUtil.getSnowflakeNextId());
        skToken.setCreateTime(now);
        skToken.setUpdateTime(now);

        int seatCount = dailyTrainSeatService.countSeat(date, trainCode);
        log.info("车次【{}】的座位数：{}", trainCode, seatCount);

        long stationCount = dailyTrainStationService.countStation(date, trainCode);
        log.info("车次【{}】的站数：{}", trainCode, stationCount);

        int count = (int)(seatCount*stationCount);
        log.info("车次【{}】的初始令牌生成数：{}", trainCode, count);
        skToken.setCount(count);

        skTokenMapper.insert(skToken);
    }

    //获取令牌
    public boolean validSkToken(Date date, String trainCode, Long memberId) throws InterruptedException {
        log.info("会员【{}】获取日期【{}】车次【{}】的令牌开始", memberId, DateUtil.formatDate(date), trainCode);

        if (!env.equals("dev")){
            String lockKey = RedisKeyPreEnum.SK_TOKEN + "-" + DateUtil.formatDate(date)+"-"+trainCode+"-"+memberId;
            //使用redisson，自带看门狗
            RLock lock = null;
            lock = redissonClient.getLock(lockKey);
            boolean tryLock = lock.tryLock(5, 10, TimeUnit.SECONDS);
            if (tryLock){
                log.info("抢到令牌锁:{}", lockKey);
            }else{
                log.info("没抢到令牌锁:{}", lockKey);
                return false;
            }
        }

        String skTokenCountKey = RedisKeyPreEnum.SK_TOKEN_COUNT + "-" + DateUtil.formatDate(date)+"-"+trainCode;
        Object skTokenCount = redisTemplate.opsForValue().get(skTokenCountKey);
        if (ObjUtil.isNotNull(skTokenCount)){
            log.info("缓存中有该车次令牌大闸的key:{}", skTokenCountKey);
            Long count = redisTemplate.opsForValue().decrement(skTokenCountKey, 1);
            if(count < 0L){
                log.info("获取令牌失败:{}", skTokenCountKey);
                return false;
            }else{
                log.info("获取令牌成功，令牌余数:{}", count);
                //令牌大闸的key有效期设置为60秒
                redisTemplate.expire(skTokenCountKey, 60, TimeUnit.SECONDS);
                if(count%5==0){
                    SkTokenExample example = new SkTokenExample();
                    example.createCriteria()
                        .andDateEqualTo(date)
                        .andTrainCodeEqualTo(trainCode);
                    List<SkToken> list = skTokenMapper.selectByExample(example);
                    int count1 = 0;
                    if (CollUtil.isNotEmpty(list)){
                        count1 = list.get(0).getCount();
                    }
                    skTokenMapperCust.decrease(date, trainCode, count1, 5);
                }
                return true;
            }
        }else{
            log.info("缓存中没有该车次令牌大闸的key:{}", skTokenCountKey);
            SkTokenExample example = new SkTokenExample();
            example.createCriteria()
                    .andDateEqualTo(date)
                    .andTrainCodeEqualTo(trainCode);
            List<SkToken> list = skTokenMapper.selectByExample(example);
            if(CollUtil.isEmpty(list)){
                log.info("找不到日期【{}】车次【{}】的令牌记录", DateUtil.formatDate(date), trainCode);
                return false;
            }
            SkToken skToken = list.get(0);
            if(skToken.getCount() <= 0){
                log.info("日期【{}】车次【{}】的令牌余量为0", DateUtil.formatDate(date), trainCode);
                return false;
            }

            Integer count = skToken.getCount() - 1;
            skToken.setCount(count);
            log.info("将该车次令牌放入缓存中，key:{}, count:{}", skTokenCountKey, count);
            redisTemplate.opsForValue().set(skTokenCountKey, String.valueOf(count), 60, TimeUnit.SECONDS);
            return true;
        }
    }
}
